﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Manager : MonoBehaviour
{
    private static Manager instance;
    public static Manager Instance
    {
        get
        {
            if(instance == null)
            {
                instance = FindObjectOfType<Manager>();
            }
            if(instance == null)
            {
                GameObject go = new GameObject();
                instance = go.AddComponent<Manager>();
                go.name = "Manager";
            }
            return instance;
        }
    }

    public GameObject agentCameta;

    public GameObject agentPrefab;
    public int totNum;

    //聚集力
    public float cohesionCoefficient = 1f;
    public float cohesionRadius = 8;

    //分散力
    public float dispersionCoefficient = 4;
    public float dispersionRadius = 3;

    //队列力
    public float queueCoefficient = 0.5f;
    public float queueRadius = 4;

    //随机力
    public float randomCoefficient = 100;

    //目标力
    public GameObject target;
    public float targetCoefficient = 1;

    public float speed = 5;
    public float viewAngle = 120;
    public float viewDistance = 8;

    private List<Agent> agentList;
    public Dictionary<Vector3Int, List<Agent>> agentDic;

    private void Start()
    {
        agentList = new List<Agent>();
        agentDic = new Dictionary<Vector3Int, List<Agent>>();

        for(int i = 0; i < totNum; ++i)
        {
            float x = Random.Range(-10f, 10f);
            float y = Random.Range(-10f, 10f);
            float z = Random.Range(-10f, 10f);
            var go = Instantiate(agentPrefab, new Vector3(x, y, z), Quaternion.identity);
            Agent agent = go.AddComponent<Agent>();
            agentList.Add(agent);
        }

        GenerateNewDic();
    }

    private void Update()
    {
        agentDic.Clear();
        GenerateNewDic();

        Vector3 aimPos = agentList[0].transform.position - agentList[0].transform.forward * 2 + Vector3.up * 0.5f;

        agentCameta.transform.position = Vector3.Lerp(agentCameta.transform.position, aimPos, 10 * Time.deltaTime);

        agentCameta.transform.LookAt(agentList[0].transform);
    }

    public List<Agent> GetAgentsInView(Agent agent, float radius)
    {
        List<Agent> targets = new List<Agent>();

        Vector3Int block = new Vector3Int((int)(agent.transform.position.x / viewDistance),
                (int)(agent.transform.position.y / viewDistance), (int)(agent.transform.position.z / viewDistance));

        CheckBlock(targets, agent, block, radius);
        CheckBlock(targets, agent, block + new Vector3Int(0, 0, 1), radius);
        CheckBlock(targets, agent, block + new Vector3Int(0, 0, -1), radius);
        CheckBlock(targets, agent, block + new Vector3Int(0, 1, 0), radius);
        CheckBlock(targets, agent, block + new Vector3Int(0, -1, 0), radius);
        CheckBlock(targets, agent, block + new Vector3Int(1, 0, 0), radius);
        CheckBlock(targets, agent, block + new Vector3Int(-1, 0, 0), radius);

        return targets;
    }

    private void CheckBlock(List<Agent> targets, Agent agent, Vector3Int block, float radius)
    {
        if (agentDic.ContainsKey(block)) {
            foreach (Agent item in agentDic[block]) {
                if (item != agent && IsInView(agent, item, radius)) {
                    targets.Add(item);
                }
            }
        }
    }

    private bool IsInView(Agent agent, Agent other, float radius)
    {
        if(Vector3.Distance(agent.transform.position, other.transform.position) <= radius
            && Vector3.Angle(agent.transform.forward, other.transform.position - agent.transform.position) <= viewAngle)
        {
            return true;
        }
        return false;
    }

    private void OnGUI()
    {
        if (GUILayout.Button("切换视角"))
        {
            agentCameta.SetActive(!agentCameta.activeInHierarchy);
        }
    }

    private void GenerateNewDic()
    {
        for (int i = 0; i < agentList.Count; ++i) {
            Vector3Int block = new Vector3Int((int)(agentList[i].transform.position.x / viewDistance),
                (int)(agentList[i].transform.position.y / viewDistance), (int)(agentList[i].transform.position.z / viewDistance));
            if (!agentDic.ContainsKey(block)) {
                agentDic[block] = new List<Agent>();
            }
            agentDic[block].Add(agentList[i]);
        }
    }
}
